package com.sinosoft.cloud.access.net;

import com.sinosoft.cloud.access.codec.Decoder;
import com.sinosoft.cloud.access.codec.DecoderFactory;
import com.sinosoft.cloud.access.crypto.CryptoBean;
import com.sinosoft.cloud.access.crypto.CryptoFactory;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import lombok.extern.apachecommons.CommonsLog;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.net.InetSocketAddress;

import static io.netty.channel.ChannelFutureListener.CLOSE;

/**
 * cloud-netty
 *
 * @title: cloud-netty
 * @package: com.sinosoft.cloud.access.net
 * @author: yangming
 * @date: 2017/12/3 下午2:47
 */
@Component
@CommonsLog
public class NettyClient {

    protected final Log logger = LogFactory.getLog(getClass());

    @Autowired
    private ClientProperties clientProperties;

    private DecoderFactory decoderFactory = DecoderFactory.getInstance();

    /**
     * 连接Router目标端.
     *
     * @param accessName
     * @param addr
     * @param ctx
     * @param msg
     */
    private void initBootstrap(String accessName, InetSocketAddress addr, final ChannelHandlerContext ctx, Object msg, MessageObject messageObject) {
        log.info("开始连接" + accessName + " " + addr.toString());
        /*由于Router目标端无高性能要求, 因此采用默认线程池数量设置*/
        EventLoopGroup workGroup = new NioEventLoopGroup();
        addShutdownHook(workGroup);
        RouterChannelInitializer routerChannelInitializer = new RouterChannelInitializer(ctx, msg, messageObject);
        routerChannelInitializer.setAccessName(accessName);
        routerChannelInitializer.setIp(addr.getHostName());
        routerChannelInitializer.setPort(addr.getPort());
        Bootstrap b = new Bootstrap();
        b.group(workGroup).channel(NioSocketChannel.class);
        b.handler(routerChannelInitializer);
//        b.bind(addr).addListener((ChannelFutureListener) future -> {
//            if (future.isSuccess()) {
//                log.info("连接Router目标端成功!" + addr.toString());
//            } else {
//                log.error("连接Router目标端失败!" + addr.toString());
//            }
//        });
        b.remoteAddress(addr);
        b.connect().addListener((ChannelFutureListener) future -> {
            if (future.isSuccess()) {
                Channel channel = future.channel();
                channel.writeAndFlush(msg);
            } else {
                log.error("连接Router目标端失败!" + addr.toString());

                String errorStr = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><ABCB2I><Header><RetCode>009999</RetCode><RetMsg>系统繁忙，请稍后重试</RetMsg><SerialNo></SerialNo><InsuSerial/><TransDate></TransDate><TranTime></TranTime><BankCode></BankCode><CorpNo></CorpNo><TransCode></TransCode></Header></ABCB2I>";

                CryptoFactory cryptoFactory = CryptoFactory.getInstance();
                CryptoBean cryptoBean = cryptoFactory.getCryptoBeanByAccess(accessName);

                if (null != cryptoBean) {
                    String originalMsg = messageObject.getOriginalMsg().substring(73);
                    if (!originalMsg.startsWith("<")) {
                        errorStr = cryptoBean.encryt(errorStr);
                    }
                }

                Decoder coder = decoderFactory.getDecoder(accessName);
                if (coder != null) {
                    errorStr = coder.encoder(errorStr);
                }

                ByteBuf encoded = Unpooled.copiedBuffer(errorStr.getBytes());
                ctx.writeAndFlush(encoded).addListener(CLOSE);
                logger.error("连接CIP异常！无法获取连接！" + System.getProperty("line.separator")
                        + "解密后的报文：" + messageObject.getDecryptMsg() + System.getProperty("line.separator")
                        + "返回的报文：" + errorStr);
            }
        });

    }

    /**
     * 想服务端发送消息
     *
     * @param accessName
     * @param ctx
     * @param msg
     */
    public void send(String accessName, ChannelHandlerContext ctx, Object msg, MessageObject messageObject) {

        if (clientProperties == null
                || clientProperties.getRouter() == null
                || clientProperties.getRouter().get(accessName) == null) {
            log.error("路由信息未配置,无法路由; 请配置 router.properties");
            return;
        }
        RouterProperties routerProperties = clientProperties.getRouter().get(accessName);
        InetSocketAddress address = new InetSocketAddress(routerProperties.getTargetIp(), routerProperties.getTargetPort());
        initBootstrap(accessName, address, ctx, msg, messageObject);
    }

    /**
     * @param workGroup
     */
    private void addShutdownHook(EventLoopGroup workGroup) {
        Runtime.getRuntime().addShutdownHook(new Thread(() -> {
            log.info("关闭Router客户端连接");
            workGroup.shutdownGracefully();
        }));
    }


}
